package com.wetool.service;

import com.wetool.Constant;
import com.wetool.common.model.ItemDto;
import com.wetool.common.model.query.ShopQueryParam;
import com.wetool.entity.Area;
import com.wetool.entity.Merchant;
import com.wetool.entity.Shop;
import com.wetool.exception.*;
import com.wetool.feign.SmsFeignClient;
import com.wetool.feign.UserFeignClient;
import com.wetool.jpa.ShopInfoRepo;
import com.wetool.jpa.ShopInfoSpec;
import com.wetool.jpa.ShopItemSpec;
import com.wetool.jpa.ShopManagementListSpec;
import com.wetool.jpa.ShopRepo;
import com.wetool.jpa.ShopSpec;
import com.wetool.model.*;
import com.wetool.model.dto.ShopInfoDto;
import com.wetool.stream.MacMessageProducer;
import com.wetool.util.BeanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.net.URLEncoder;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 业务处理类（店铺）
 *
 * @author zhangjie
 */
@Component
public class ShopService {

    private static final Logger LOGGER = LogManager.getLogger(ShopService.class);

    @Autowired
    private MerchantService merchantService;

    @Autowired
    private ShopRepo shopRepo;
    
    @Autowired
    private ShopInfoRepo shopInfoRepo;

    @Autowired
    private AreaService areaService;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private MacService macService;

    @Autowired
    private LoginService loginService;

    @Autowired
    private CaptchaService captchaService;

    @Autowired
    private SmsFeignClient smsFeignClient;

    @Autowired
    private MacMessageProducer macMessageProducer;

    @Autowired
    private SettingsService settingsService;

    /**
     * 店铺查询
     *
     * @param param 查询参数
     * @param p     分页参数
     * @return 店铺对象集合
     */
    public Page<Shop> find(SearchParam param, Pageable p) {
        ShopSpec spec = new ShopSpec(param);
        Page<Shop> shops = shopRepo.findAll(spec, p);
        return shops;
    }
    /**
     * 查询附近商家
     *
     * @param param  p 查询参数
     * @return Page<ShopInfoDto> shopInfos 店铺对象集合
     */
    public Page<ShopInfoDto> findSurroundShop(SearchParam param,Pageable p) {
    	ShopInfoSpec spec = new ShopInfoSpec(param);
    	Page<ShopInfoDto> shopInfos = shopInfoRepo.findAll(spec,p);
    	shopInfos.getContent().stream().forEach(sh -> {
    		ShopInfoDto.setDistance(sh.getLongitude(), sh.getLatitude(), param);
    	});
    	return shopInfos;
    }

    /**
     * 店铺查询
     *
     * @param id 店铺id（主键）
     * @return 店铺对象
     */
    public Shop findOne(Long id) {
        return shopRepo.findOne(id);
    }

    /**
     * 店铺查询
     *
     * @param username 店铺主帐号
     * @return 店铺对象
     */
    public Shop findByUsername(String username) {
        return this.shopRepo.findByUsername(username);
    }

    /**
     * 店铺新增
     *
     * @param param 请求参数
     * @return
     * @throws AccountExistedException
     * @throws Exception
     */
    public Shop add(ShopRegister param) throws AccountExistedException, Exception {

    	/* 校验设备信息是否正常 */
        if (!macService.verifyMacSn(param.getMacSn())) {
            throw new MacNotFoundException();
        }

		/* 获取商家对象 */
        Merchant merchant = merchantService.findOne(param.getMerchantId());
        return this.addAndBindMac(param, merchant);
    }

    public Shop addWithoutMac(ShopRegister param, Merchant merchant, Area area) throws Exception {
        Shop shop = new Shop();
        BeanUtils.copyProperties(param, shop);
        shop.setMerchant(merchant); // 关联店铺所属商家
        shop.setLogoUrl(Constant.DEFAULT_LOGO_URL);

		/* 省/市/区 处理 */
        shop.setArea(area);
        shop = shopRepo.saveAndFlush(shop);

        this.createAcount(shop, param.getPassword(), false);
        return shop;
    }
    
    public Shop addAndBindMac(ShopRegister param, Merchant merchant) throws Exception {
        Shop shop = new Shop();
        BeanUtils.copyProperties(param, shop);
        shop.setMerchant(merchant); // 关联店铺所属商家
        shop.setLogoUrl(Constant.DEFAULT_LOGO_URL);

		/* 省/市/区 处理 */
        if (param.getDistrict() != null && param.getCity() != null && param.getProvince() != null) {
            Area area = areaService.getArea(param.getProvince(), param.getCity(), param.getDistrict());
            shop.setArea(area);
        }
        shop = shopRepo.saveAndFlush(shop);

        this.createAcount(shop, param.getPassword(), true);

		/* 绑定设备 */
        macService.bind(shop.getMerchant().getId(), shop.getId(), param.getMacSn());
        return shop;
    }

    /**
     * 创建店铺帐号
     *
     * @throws Exception
     */
    public void createAcount(Shop shop, String password, boolean sendSms) throws Exception {
        String username = shop.getUsername();
        /* 店铺帐号已存在，不允许重复创建 */
        if (!StringUtils.isEmpty(username)) {
            throw new AccountExistedException();
        }

		/* 自动创建 */
        username = this.fastRegister(password);

		/* 更新店铺用户帐号 */
        shop.setUsername(username);
        shopRepo.saveAndFlush(shop);

        // 下发短信
        if(sendSms) {
        	StringBuffer sms = new StringBuffer();
            sms.append(shop.getUsername()).append("/").append(password);
            smsFeignClient.sendSms(shop.getMobile(), Constant.SMS_TYPE_OK_REGISTER2, URLEncoder.encode(sms.toString()));    // 下发注册成功短信通知	
        }
    }

    /**
     * 创建店铺帐号（快速创建）
     *
     * @throws Exception
     */
    private String fastRegister(String password) throws Exception {
        /* 创建店铺帐号 */
        Acount acount = new Acount(password, Constant.USER_SHOP, Constant.ROLE_KEY_SHOP);
        ResponseEntity<Message<?>> response = userFeignClient.fastRegister(acount);
        Message<?> message = response.getBody();
        switch (message.getCode()) {
            case 0: // 成功状态
                Map<String, Object> data = (Map<String, Object>) message.getData();
                String username = String.valueOf(data.get("username"));
                LOGGER.info("店铺帐号创建完成（Auto）, username={}", username);
                return username;
            case 3: // 帐号已存在
                throw new AccountExistedException();
            default: // 其它状态码全部判定为异常
                throw new Exception();
        }
    }

    /**
     * 店铺登录（隐式登录，自动注册店铺帐号后执行）
     *
     * @param shopId 店铺ID
     * @return
     * @throws Exception
     */
    public Map<String, Object> login(Long shopId) throws Exception {
        Shop shop = shopRepo.findOne(shopId);
        String username = shop.getUsername();
        ResponseEntity<Message<?>> response = userFeignClient.getPassword(username);
        Message<?> message = response.getBody();
        switch (message.getCode()) {
            case 0: // 成功状态
                Map<String, String> data = (Map<String, String>) message.getData();
                return this.login(shop, data.get("password"));
            case 4: // 帐号不存在
                throw new AcountNotFoundException();
            default: // 其它状态码全部判定为异常
                throw new Exception();
        }
    }

    /**
     * 忘记密码
     *
     * @param pwdForgot
     * @return
     * @throws Exception
     */
    public Token pwdForgot(PwdForgot pwdForgot) throws Exception {

		/* 校验手机号与设备号所属店铺手机是否匹配 */
        String mobile = pwdForgot.getMobile();
        Long shopId = verify(pwdForgot.getMacSn(), mobile);

		/* 校验验证码 */
        String key = Constant.CATCHA_PWD + mobile; // 密码找回验证码Key=P_手机号
        captchaService.verify(key, pwdForgot.getCaptcha());
        Map<String, Object> map = login(shopId);
        return (Token) map.get("token");
    }

    /**
     * 修改密码
     *
     * @param pwdModify
     * @return
     * @throws Exception
     */
    public void setPassword(PwdModify pwdModify) throws Exception {
        String password = pwdModify.getPassword();
        Long shopId = macService.getShopId(pwdModify.getMacSn());
        Shop shop = this.findOne(shopId);
        String username = shop.getUsername();

        ResponseEntity<Message<?>> response = userFeignClient.setPassword(new Acount(username, password));
        Message<?> message = response.getBody();
        switch (message.getCode()) {
            case 0: // 成功状态
                LOGGER.info("密码修改成功！");
                break;
            default: // 其它状态码全部判定为异常
                throw new Exception();
        }
    }

    /**
     * 店铺登录
     *
     * @param macSn    设备号
     * @param password 密码
     * @return
     * @throws Exception
     */
    public Map<String, Object> macSnLogin(String macSn, String password) throws Exception {
        Long shopId = macService.getShopId(macSn);
        Shop shop = shopRepo.findOne(shopId);
        return this.login(shop, password);
    }

    /**
     * 店铺登录
     *
     * @param username
     * @param password
     * @return
     * @throws Exception
     */
    public Map<String, Object> login(String username, String password) throws Exception {
        Shop shop = shopRepo.findByUsername(username);
        if (shop == null) {
            throw new ShopAcountRelationException();
        }
        return login(shop, password);
    }

    /**
     * 店铺登录
     *
     * @param shop
     * @param password
     * @return
     * @throws Exception
     */
    private Map<String, Object> login(Shop shop, String password) throws Exception {
        Token token = loginService.login(shop.getUsername(), password);
        return new HashMap<String, Object>() {
            private static final long serialVersionUID = 1L;

            {
                put("token", token);
            }

            {
                put("merchantId", shop.getMerchant().getId());
            }    //商家ID

            {
                put("shopId", shop.getId());
            }    //店铺ID

            {
                put("logoUrl", shop.getLogoUrl());
            }    //店铺logo

            {
                put("name", shop.getName());
            }    //店铺名称

            {
                put("longitude", shop.getLongitude());
            }    // 经度

            {
                put(" latitude", shop.getLatitude());
            }        // 纬度

            {
                put("address", shop.getAddress());
            }

            {
                put("area", shop.getArea());
            }
        };
    }

    public Shop update(Long id, ShopModify param) throws ShopNotFoundException {
        Shop shop = shopRepo.findOne(id);
        if (shop == null) {
            throw new ShopNotFoundException();
        }
        BeanUtils.copyProperties(param, shop);

		/* 省/市/区 处理 */
        if (param.getDistrict() != null && param.getCity() != null && param.getProvince() != null) {
            Area newArea = areaService.getArea(param.getProvince(), param.getCity(), param.getDistrict());
            Area oldArea = shop.getArea();
            BeanUtils.copyProperties(newArea, oldArea);
            shop.setArea(oldArea);
        }

        return (Shop) this.shopRepo.saveAndFlush(shop);
    }

    public void delete(Long id) throws ShopNotFoundException {
        Shop shop = shopRepo.findOne(id);
        if (shop == null) {
            throw new ShopNotFoundException();
        }
        shop.setDeleted(true);    // 逻辑删除
        shopRepo.saveAndFlush(shop);
        List<String> macSns = settingsService.findByShopId(id);
        for (String macSn : macSns) {
            macMessageProducer.sendAdvertMacMessage(macSn, shop, shop.getMerchant(), true);
        }
    }

    /**
     * 验证入参手机号是否与设备号所属店铺手机匹配
     *
     * @param macSn
     * @param mobile
     * @return 店铺Id
     * @throws Exception
     */
    public Long verify(String macSn, String mobile) throws Exception {
        Long shopId = macService.getShopId(macSn);
        Shop shop = this.findOne(shopId);
        if (!mobile.equals(shop.getMobile())) {
            throw new MobileVerifyFailedException("手机号与设备号所属店铺手机不匹配");
        }
        return shopId;
    }

    /**
     * 手机号码更改验证
     *
     * @param param 请求参数
     * @throws Exception
     */
    public void changePhoneVerify(ShopChangePhone param) throws Exception {
        String mobile = param.getMobile();
        /* 校验验证码 */
        String key = Constant.CATCHA_CHANGE_VERIFY + mobile; // 商家登录验证码Key=L_手机号
        captchaService.verify(key, param.getCaptcha());

        Shop shop = this.shopRepo.findOne(param.getId());
        if (shop == null) {
            LOGGER.debug("根据店铺id【{}】找不到对应的店铺.", param.getId());
            throw new MerchantNotFoundException();
        }
        if (!shop.getMobile().equals(param.getMobile())) {
            LOGGER.debug("当前店铺手机号【{}】，与请求手机号不符【{}】", shop.getMobile(), param.getMobile());
            throw new IllegalArgumentException();
        }
    }

    /**
     * 手机号码更改确认
     *
     * @param param 参数
     * @throws Exception
     */
    public void changePhoneConfirm(ShopChangePhone param) throws Exception {
        String mobile = param.getMobile();
        /* 校验验证码 */
        String key = Constant.CATCHA_CHANGE_CONFIRM + mobile; // 商家登录验证码Key=L_手机号
        captchaService.verify(key, param.getCaptcha());
        Shop shop = this.shopRepo.findOne(param.getId());
        shop.setMobile(param.getMobile());
        this.shopRepo.save(shop);
    }

    /**
     * 更具id查询店铺信息是否被删除
     *
     * @param shopId
     * @param isDeleted
     * @return
     */
    public Shop getByIdAndIsDeleted(Long shopId, boolean isDeleted) {
        return this.shopRepo.getByIdAndIsDeleted(shopId, isDeleted);
    }

    /**
     * 查询店铺列表项
     *
     * @param merchantId
     * @param name
     * @return
     */
    public Message<?> queryItemList(Long merchantId, String name) {
        List<Shop> shopList = this.shopRepo.findAll(new ShopItemSpec(name, merchantId), new PageRequest(0, 50)).getContent();

        List<ItemDto> collect = shopList.stream()
                .map(shop -> {
                    ItemDto node = new ItemDto(shop.getId(), shop.getName());
//                    node.setParent(new ItemDto(shop.getMerchant().getId(), shop.getMerchant().getName()));
                    return node;
                }).sorted(Comparator.comparing(ItemDto::getName)).collect(Collectors.toList());
        return new Message<>(Message.Result.SUCCESS, collect);
    }

    /**
     * 查询指定id的列表项
     *
     * @param shopId
     * @return
     */
    public Message<?> queryItem(Long shopId) {
        Shop shop = this.shopRepo.findOne(shopId);

        if (shop == null) {
            return new Message<>(Message.Result.ERROR, "未找到店铺：shopId=" + shopId);
        }

        return new Message<>(Message.Result.SUCCESS, new ItemDto(shop.getId(), shop.getName()));
    }


    /**
     * 查询店铺管理列表展示项
     *
     * @param shopQueryParam
     * @param pageable
     * @return
     */
    public Message<?> queryShopManagementListItem(ShopQueryParam shopQueryParam, Pageable pageable) {


        ShopManagementListSpec shopManagementListSpec = new ShopManagementListSpec(shopQueryParam);

        this.shopRepo.findAll(shopManagementListSpec, pageable);


        //TODO

        return new Message<>(Message.Result.SUCCESS);
    }
}